{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "W_tvPdyfA-BL"
      },
      "source": [
        "##### Copyright 2018 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "0O_LFhwSBCjm"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "PWUmcKKjtwXL"
      },
      "source": [
        "# Transfer learning with TensorFlow Hub\n",
        "\n",
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://www.tensorflow.org/tutorials/images/transfer_learning_with_hub\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />View on TensorFlow.org</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs/blob/master/site/en/tutorials/images/transfer_learning_with_hub.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Run in Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs/blob/master/site/en/tutorials/images/transfer_learning_with_hub.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />View on GitHub</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a href=\"https://storage.googleapis.com/tensorflow_docs/docs/site/en/tutorials/images/transfer_learning_with_hub.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\" />Download notebook</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a href=\"https://tfhub.dev/google/tf2-preview/mobilenet_v2/feature_vector/4\"><img src=\"https://www.tensorflow.org/images/hub_logo_32px.png\" />See TF Hub model</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "crU-iluJIEzw"
      },
      "source": [
        "[TensorFlow Hub](https://tfhub.dev/) is a repository of pre-trained TensorFlow models.\n",
        "\n",
        "This tutorial demonstrates how to:\n",
        "\n",
        "1. Use models from TensorFlow Hub with `tf.keras`\n",
        "1. Use an image classification model from TensorFlow Hub\n",
        "1. Do simple transfer learning to fine-tune a model for your own image classes"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CKFUvuEho9Th"
      },
      "source": [
        "## Setup"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "OGNpmn43C0O6"
      },
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import time\n",
        "\n",
        "import PIL.Image as Image\n",
        "import matplotlib.pylab as plt\n",
        "\n",
        "import tensorflow as tf\n",
        "import tensorflow_hub as hub"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "s4YuF5HvpM1W"
      },
      "source": [
        "## An ImageNet classifier\n",
        "\n",
        "You'll start by using a pretrained classifer model to take an image and predict what it's an image of - no training required!"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xEY_Ow5loN6q"
      },
      "source": [
        "### Download the classifier\n",
        "\n",
        "Use `hub.KerasLayer` to load a [MobileNetV2 model](https://tfhub.dev/google/tf2-preview/mobilenet_v2/classification/2) from TensorFlow Hub. Any [compatible image classifier model](https://tfhub.dev/s?q=tf2&module-type=image-classification) from TensorFlow Hub will work here."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "feiXojVXAbI9"
      },
      "outputs": [],
      "source": [
        "classifier_model =\"https://tfhub.dev/google/tf2-preview/mobilenet_v2/classification/4\" #@param {type:\"string\"}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "y_6bGjoPtzau"
      },
      "outputs": [],
      "source": [
        "IMAGE_SHAPE = (224, 224)\n",
        "\n",
        "classifier = tf.keras.Sequential([\n",
        "    hub.KerasLayer(classifier_model, input_shape=IMAGE_SHAPE+(3,))\n",
        "])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "pwZXaoV0uXp2"
      },
      "source": [
        "### Run it on a single image"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "TQItP1i55-di"
      },
      "source": [
        "Download a single image to try the model on."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "w5wDjXNjuXGD"
      },
      "outputs": [],
      "source": [
        "grace_hopper = tf.keras.utils.get_file('image.jpg','https://storage.googleapis.com/download.tensorflow.org/example_images/grace_hopper.jpg')\n",
        "grace_hopper = Image.open(grace_hopper).resize(IMAGE_SHAPE)\n",
        "grace_hopper"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "BEmmBnGbLxPp"
      },
      "outputs": [],
      "source": [
        "grace_hopper = np.array(grace_hopper)/255.0\n",
        "grace_hopper.shape"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "0Ic8OEEo2b73"
      },
      "source": [
        "Add a batch dimension, and pass the image to the model."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "EMquyn29v8q3"
      },
      "outputs": [],
      "source": [
        "result = classifier.predict(grace_hopper[np.newaxis, ...])\n",
        "result.shape"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NKzjqENF6jDF"
      },
      "source": [
        "The result is a 1001 element vector of logits, rating the probability of each class for the image.\n",
        "\n",
        "So the top class ID can be found with argmax:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "rgXb44vt6goJ"
      },
      "outputs": [],
      "source": [
        "predicted_class = np.argmax(result[0], axis=-1)\n",
        "predicted_class"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "YrxLMajMoxkf"
      },
      "source": [
        "### Decode the predictions\n",
        "\n",
        "Take the predicted class ID and fetch the `ImageNet` labels to decode the predictions"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ij6SrDxcxzry"
      },
      "outputs": [],
      "source": [
        "labels_path = tf.keras.utils.get_file('ImageNetLabels.txt','https://storage.googleapis.com/download.tensorflow.org/data/ImageNetLabels.txt')\n",
        "imagenet_labels = np.array(open(labels_path).read().splitlines())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "uzziRK3Z2VQo"
      },
      "outputs": [],
      "source": [
        "plt.imshow(grace_hopper)\n",
        "plt.axis('off')\n",
        "predicted_class_name = imagenet_labels[predicted_class]\n",
        "_ = plt.title(\"Prediction: \" + predicted_class_name.title())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "amfzqn1Oo7Om"
      },
      "source": [
        "## Simple transfer learning"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "K-nIpVJ94xrw"
      },
      "source": [
        "But what if you want to train a classifier for a dataset with different classes? You can also use a model from TFHub to train a custom image classier by retraining the top layer of the model to recognize the classes in our dataset."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Z93vvAdGxDMD"
      },
      "source": [
        "### Dataset\n",
        "\n",
        " For this example you will use the TensorFlow flowers dataset:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "DrIUV3V0xDL_"
      },
      "outputs": [],
      "source": [
        "data_root = tf.keras.utils.get_file(\n",
        "  'flower_photos','https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz',\n",
        "   untar=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "jFHdp18ccah7"
      },
      "source": [
        "Let's load this data into our model using  images off disk using image_dataset_from_directory."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "mqnsczfLgcwv"
      },
      "outputs": [],
      "source": [
        "batch_size = 32\n",
        "img_height = 224\n",
        "img_width = 224\n",
        "\n",
        "train_ds = tf.keras.preprocessing.image_dataset_from_directory(\n",
        "  str(data_root),\n",
        "  validation_split=0.2,\n",
        "  subset=\"training\",\n",
        "  seed=123,\n",
        "  image_size=(img_height, img_width),\n",
        "  batch_size=batch_size)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "cCrSRlomEIZ4"
      },
      "source": [
        "The flowers dataset has five classes."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "AFgDHs6VEFRD"
      },
      "outputs": [],
      "source": [
        "class_names = np.array(train_ds.class_names)\n",
        "print(class_names)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "L0Btd0V3C8h4"
      },
      "source": [
        "TensorFlow Hub's conventions for image models is to expect float inputs in the `[0, 1]` range. Use the `Rescaling` layer to achieve this."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Rs6gfO-ApTQW"
      },
      "source": [
        "Note: you could also include the `Rescaling` layer inside the model. See this [guide](https://www.tensorflow.org/guide/keras/preprocessing_layers) for a discussion of the tradeoffs."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "8NzDDWEMCL20"
      },
      "outputs": [],
      "source": [
        "normalization_layer = tf.keras.layers.experimental.preprocessing.Rescaling(1./255)\n",
        "train_ds = train_ds.map(lambda x, y: (normalization_layer(x), y))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "IW-BUJ-NC7y-"
      },
      "source": [
        "Let's make sure to use buffered prefetching so we can yield data from disk without having I/O become blocking. These are two important methods you should use when loading data.\n",
        "\n",
        "Interested readers can learn more about both methods, as well as how to cache data to disk in the [data performance guide](https://www.tensorflow.org/guide/data_performance#prefetching)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ZmJMKFw7C4ki"
      },
      "outputs": [],
      "source": [
        "AUTOTUNE = tf.data.AUTOTUNE\n",
        "train_ds = train_ds.cache().prefetch(buffer_size=AUTOTUNE)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "m0JyiEZ0imgf"
      },
      "outputs": [],
      "source": [
        "for image_batch, labels_batch in train_ds:\n",
        "  print(image_batch.shape)\n",
        "  print(labels_batch.shape)\n",
        "  break"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "0gTN7M_GxDLx"
      },
      "source": [
        "### Run the classifier on a batch of images"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "O3fvrZR8xDLv"
      },
      "source": [
        "Now run the classifier on the image batch."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "pcFeNcrehEue"
      },
      "outputs": [],
      "source": [
        "result_batch = classifier.predict(train_ds)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "-wK2ky45hlyS"
      },
      "outputs": [],
      "source": [
        "predicted_class_names = imagenet_labels[np.argmax(result_batch, axis=-1)]\n",
        "predicted_class_names"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "QmvSWg9nxDLa"
      },
      "source": [
        "Now check how these predictions line up with the images:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "IXTB22SpxDLP"
      },
      "outputs": [],
      "source": [
        "plt.figure(figsize=(10,9))\n",
        "plt.subplots_adjust(hspace=0.5)\n",
        "for n in range(30):\n",
        "  plt.subplot(6,5,n+1)\n",
        "  plt.imshow(image_batch[n])\n",
        "  plt.title(predicted_class_names[n])\n",
        "  plt.axis('off')\n",
        "_ = plt.suptitle(\"ImageNet predictions\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "FUa3YkvhxDLM"
      },
      "source": [
        "See the `LICENSE.txt` file for image attributions.\n",
        "\n",
        "The results are far from perfect, but reasonable considering that these are not the classes the model was trained for (except \"daisy\")."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "JzV457OXreQP"
      },
      "source": [
        "### Download the headless model\n",
        "\n",
        "TensorFlow Hub also distributes models without the top classification layer. These can be used to easily do transfer learning.\n",
        "\n",
        "Any [compatible image feature vector model](https://tfhub.dev/s?module-type=image-feature-vector&q=tf2) from TensorFlow Hub will work here."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "4bw8Jf94DSnP"
      },
      "outputs": [],
      "source": [
        "feature_extractor_model = \"https://tfhub.dev/google/tf2-preview/mobilenet_v2/feature_vector/4\" #@param {type:\"string\"}"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "sgwmHugQF-PD"
      },
      "source": [
        "Create the feature extractor. Use `trainable=False` to freeze the variables in the feature extractor layer, so that the training only modifies the new classifier layer."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "5wB030nezBwI"
      },
      "outputs": [],
      "source": [
        "feature_extractor_layer = hub.KerasLayer(\n",
        "    feature_extractor_model, input_shape=(224, 224, 3), trainable=False)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "0QzVdu4ZhcDE"
      },
      "source": [
        "It returns a 1280-length vector for each image:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Of7i-35F09ls"
      },
      "outputs": [],
      "source": [
        "feature_batch = feature_extractor_layer(image_batch)\n",
        "print(feature_batch.shape)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "RPVeouTksO9q"
      },
      "source": [
        "### Attach a classification head\n",
        "\n",
        "Now wrap the hub layer in a `tf.keras.Sequential` model, and add a new classification layer."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "vQq_kCWzlqSu"
      },
      "outputs": [],
      "source": [
        "num_classes = len(class_names)\n",
        "\n",
        "model = tf.keras.Sequential([\n",
        "  feature_extractor_layer,\n",
        "  tf.keras.layers.Dense(num_classes)\n",
        "])\n",
        "\n",
        "model.summary()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "IyhX4VCFmzVS"
      },
      "outputs": [],
      "source": [
        "predictions = model(image_batch)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "FQdUaTkzm3jQ"
      },
      "outputs": [],
      "source": [
        "predictions.shape"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "OHbXQqIquFxQ"
      },
      "source": [
        "### Train the model\n",
        "\n",
        "Use compile to configure the training process:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "4xRx8Rjzm67O"
      },
      "outputs": [],
      "source": [
        "model.compile(\n",
        "  optimizer=tf.keras.optimizers.Adam(),\n",
        "  loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),\n",
        "  metrics=['acc'])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "58-BLV7dupJA"
      },
      "source": [
        "Now use the `.fit` method to train the model.\n",
        "\n",
        "To keep this example short train just 2 epochs. To visualize the training progress, use a custom callback to log the loss and accuracy of each batch individually, instead of the epoch average."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "JI0yAKd-nARd"
      },
      "outputs": [],
      "source": [
        "class CollectBatchStats(tf.keras.callbacks.Callback):\n",
        "  def __init__(self):\n",
        "    self.batch_losses = []\n",
        "    self.batch_acc = []\n",
        "\n",
        "  def on_train_batch_end(self, batch, logs=None):\n",
        "    self.batch_losses.append(logs['loss'])\n",
        "    self.batch_acc.append(logs['acc'])\n",
        "    self.model.reset_metrics()\n",
        "\n",
        "batch_stats_callback = CollectBatchStats()\n",
        "\n",
        "history = model.fit(train_ds, epochs=2,\n",
        "                    callbacks=[batch_stats_callback])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Kd0N272B9Q0b"
      },
      "source": [
        "Now after, even just a few training iterations, we can already see that the model is making progress on the task."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "A5RfS1QIIP-P"
      },
      "outputs": [],
      "source": [
        "plt.figure()\n",
        "plt.ylabel(\"Loss\")\n",
        "plt.xlabel(\"Training Steps\")\n",
        "plt.ylim([0,2])\n",
        "plt.plot(batch_stats_callback.batch_losses)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "3uvX11avTiDg"
      },
      "outputs": [],
      "source": [
        "plt.figure()\n",
        "plt.ylabel(\"Accuracy\")\n",
        "plt.xlabel(\"Training Steps\")\n",
        "plt.ylim([0,1])\n",
        "plt.plot(batch_stats_callback.batch_acc)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "kb__ZN8uFn-D"
      },
      "source": [
        "### Check the predictions\n",
        "\n",
        "To redo the plot from before, first get the ordered list of class names:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "JGbEf5l1I4jz"
      },
      "outputs": [],
      "source": [
        "predicted_batch = model.predict(image_batch)\n",
        "predicted_id = np.argmax(predicted_batch, axis=-1)\n",
        "predicted_label_batch = class_names[predicted_id]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CkGbZxl9GZs-"
      },
      "source": [
        "Plot the result"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "hW3Ic_ZlwtrZ"
      },
      "outputs": [],
      "source": [
        "plt.figure(figsize=(10,9))\n",
        "plt.subplots_adjust(hspace=0.5)\n",
        "for n in range(30):\n",
        "  plt.subplot(6,5,n+1)\n",
        "  plt.imshow(image_batch[n])\n",
        "  plt.title(predicted_label_batch[n].title())\n",
        "  plt.axis('off')\n",
        "_ = plt.suptitle(\"Model predictions\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "uRcJnAABr22x"
      },
      "source": [
        "## Export your model\n",
        "\n",
        "Now that you've trained the model, export it as a SavedModel for use later on."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "PLcqg-RmsLno"
      },
      "outputs": [],
      "source": [
        "t = time.time()\n",
        "\n",
        "export_path = \"/tmp/saved_models/{}\".format(int(t))\n",
        "model.save(export_path)\n",
        "\n",
        "export_path"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "AhQ9liIUsPsi"
      },
      "source": [
        "Now confirm that we can reload it, and it still gives the same results:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "7nI5fvkAQvbS"
      },
      "outputs": [],
      "source": [
        "reloaded = tf.keras.models.load_model(export_path)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "jor83-LqI8xW"
      },
      "outputs": [],
      "source": [
        "result_batch = model.predict(image_batch)\n",
        "reloaded_result_batch = reloaded.predict(image_batch)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "dnZO14taYPH6"
      },
      "outputs": [],
      "source": [
        "abs(reloaded_result_batch - result_batch).max()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "TYZd4MNiV3Rc"
      },
      "source": [
        "This SavedModel can be loaded for inference later, or converted to [TFLite](https://www.tensorflow.org/lite/convert/) or [TFjs](https://github.com/tensorflow/tfjs-converter).\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "mSBRrW-MqBbk"
      },
      "source": [
        "## Learn more\n",
        "\n",
        "Check out more [tutorials](https://www.tensorflow.org/hub/tutorials) for using image models from TensorFlow Hub."
      ]
    }
  ],
  "metadata": {
    "accelerator": "GPU",
    "colab": {
      "collapsed_sections": [
        "W_tvPdyfA-BL"
      ],
      "name": "transfer_learning_with_hub.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
